﻿using IoTClient.Clients.Modbus;
using IoTClient.Clients.PLC;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace IoTClient
{
    /// <summary>
    /// 自动通讯类
    /// </summary>
    public class Auto
    {
        /// <summary>
        /// 表示解析是否完全正确
        /// </summary>
        public static bool IsReady { get; set; } = false;

        /// <summary>
        /// 所有的设备集合,需要的数据都在这里,只能通过读取配置文件得到
        /// </summary>
        public static AllDeviceList AllDeviceList { get; set; } = new AllDeviceList();

        /// <summary>
        /// 所有的变量都在这里;验证的时候会被初始化
        /// </summary>
        public static Dictionary<string, Variable> VarableDictionary { get; set; } = new Dictionary<string, Variable>();

        /// <summary>
        /// 所有的变量的值都在这里;验证的时候会被初始化
        /// </summary>
        public static Dictionary<string, string> ValueDictionary { get; set; } = new Dictionary<string, string>();

        /// <summary>
        /// 根据配置文件启动所有的设备,会自动先断开所有的设备
        /// </summary>
        /// <param name="Alarm">报警产生后执行的委托</param>
        /// <param name="WarningLog">连接异常等发生后触发的委托</param>
        /// <param name="path"></param>
        /// <returns>返回为true的话,表示所有的设备配置都正常,有一个异常,结果都为false,信息见Err清单,可判断具体结果</returns>
        public static Result Start(Action<Variable, AlarmEventArgs> Alarm = null, LoggerDelegate WarningLog = null, string path = "Setting.xml")
        {
            Result result = new Result();

            AllDeviceList.Stop();

            //读取配置文件
            var cfg = LoadCfg(path);
            if (cfg.IsSucceed)//读取成功
            {
                var res = CheckCfg(cfg.Value);
                if (res.IsSucceed)//验证成功
                {
                    result.SetErrInfo(AllDeviceList.Start(Alarm, WarningLog));
                    return result;
                }
                else
                {
                    result.IsSucceed = false;
                    return result;
                }
            }
            else
            {
                result.SetErrInfo(cfg);
                return result;
            }
        }

        /// <summary>
        /// 停止所有的设备
        /// </summary>
        /// <returns></returns>
        public static Result Stop()
        {
            Result result = new Result();

            AllDeviceList.Stop();

            return result;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="devs"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static Result Save(AllDeviceList devs, string path = "Setting.xml")
        {
            Result result = new Result();
            result.IsSucceed = false;
            if (devs.SocketDeviceList.Count <= 0 && devs.SerialPortDeviceList.Count <= 0)
            {
                MessageBox.Show("一个设备都没有,你保存个寂寞?");

                return result;
            }

            IEnumerable<string> names = devs.SocketDeviceList.GroupBy(g => g.Name).Where(g => g.Count() > 1).Select(g => g.Key);
            if (names.Count() > 0)
            {
                string err = "";
                result.IsSucceed = false;
                foreach (var item in names)
                {
                    err += item + "  ";
                }
                MessageBox.Show(" 设备名字重复:" + err);
                return result;
            }
            foreach (var dev in devs.SocketDeviceList)
            {
                if (dev.AllVarableList.Count <= 0)
                {
                    MessageBox.Show($"{dev.Name}一个变量都没有,你保存个寂寞?");
                    return result;
                }
                var adds = dev.AllVarableList.GroupBy(x => x.Address).Where(x => x.Count() > 1).Select(x => x.Key);
                if (adds.Count() > 0)
                {
                    string err = "";
                    foreach (var item in adds)
                    {
                        err += item + "  ";
                    }
                    MessageBox.Show($"{dev.Name}的变量有重复的:" + err);
                    return result;
                }
            }
            //todo串口
            foreach (var dev in devs.SerialPortDeviceList)
            {
                //if (dev.AllVarableList.Count <= 0)
                //{
                //    MessageBox.Show($"{dev.Name}一个变量都没有,你保存个寂寞?");
                //    return result;
                //}
            }

            // 写入配置文件
            try
            {
                using (StreamWriter st = new StreamWriter(path))
                {
                    try
                    {
                        XmlSerializer xml = new XmlSerializer(typeof(AllDeviceList));
                        xml.Serialize(st, devs);
                        result.IsSucceed = true;
                    }
                    catch (Exception ex)
                    {
                        result.IsSucceed = false;
                        result.Exception = ex;
                        MessageBox.Show(ex.Message);
                        return result;
                    }
                }
            }
            catch (Exception ex)
            {
                result.IsSucceed = false;
                result.Exception = ex;
                MessageBox.Show(ex.Message);
                return result;
            }
            return result;
        }

        /// <summary>
        /// 读取配置文件,不会对数据做详细验证,序列化正确就返回成功
        /// </summary>
        /// <param name="path">默认路径为跟目录Setting.xml</param>
        /// <returns></returns>
        public static Result<AllDeviceList> LoadCfg(string path = "Setting.xml")
        {
            var Cfg = new Result<AllDeviceList>() { Value = new AllDeviceList() };
            //  读取配置文件
            try
            {
                using (StreamReader sr = new StreamReader(path))
                {
                    XmlSerializer xml = new XmlSerializer(typeof(AllDeviceList));
                    //原理反射:先根据初始化数据实例化类,在根据xml数据赋值
                    Cfg.Value = (AllDeviceList)xml.Deserialize(sr);
                }
            }
            catch (Exception ex)
            {
                Cfg.IsSucceed = false;
                Cfg.Exception = ex;
                Cfg.Err = $"读取配置文件失败:{ex.Message}";
                Cfg.Value = new AllDeviceList();
                return Cfg;
            }

            return Cfg;
        }

        /// <summary>
        /// 验证配置文件中的数据是否符合要求(设备名是否重复,变量地址是否重复);
        /// 如果验证成功,会给我们的系统Auto.AllDeviceList赋值
        /// </summary>
        /// <param name="allDeviceList"></param>
        /// <returns></returns>
        public static Result CheckCfg(AllDeviceList allDeviceList)
        {
            Result result = new Result();

            if (allDeviceList.SocketDeviceList.Count < 0 && allDeviceList.SerialPortDeviceList.Count < 0)
            {
                result.IsSucceed = false;
                result.Err = "设备数量为0";
                return result;
            }
            //验证读取的是否符合要求

            //1.验证网口设备,确认地址不重复
            foreach (var dev in allDeviceList.SocketDeviceList)//遍历所有网口设备
            {
                try
                {
                    dev.IpEndPoint = new IPEndPoint(IPAddress.Parse(dev.IpAddress), dev.Port);
                }
                catch (Exception ex)
                {
                    result.IsSucceed = false;
                    result.Err = $"{dev.Name}的IP地址或者端口错误";
                    result.Exception = ex;
                }
                if (dev.AllVarableList.Count <= 0)
                {
                    result.IsSucceed = false;
                    result.Err = $"{dev.Name}一个变量都没有";
                }

                foreach (var varable in dev.AllVarableList)//遍历设备的所有变量
                {
                    //todo 这里调用每个设备的验证地址是否合法的方法
                    if (!dev.VarableAddressList.ContainsKey(varable.Address))
                    {
                        dev.VarableAddressList.Add(varable.Address, varable.DataType);
                    }
                    else
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个变量地址有重复";
                        continue;
                    }
                    if (!dev.AllVarableDictionary.ContainsKey((dev.Name + "_" + varable.Address).ToUpper().Trim()))//符合就加入到字典集合中
                    {
                        dev.AllVarableDictionary.Add((dev.Name + "_" + varable.Address).ToUpper().Trim(), varable);
                    }
                    else//不符合就将结果返回false
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个地址有重复";
                    }
                    if (!Auto.ValueDictionary.ContainsKey((dev.Name + "_" + varable.Address).ToUpper().Trim()))//符合就加入到字典集合中
                    {
                        Auto.ValueDictionary.Add((dev.Name + "_" + varable.Address).ToUpper().Trim(), "NA");
                    }
                    else
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个地址有重复";
                    }
                    if (!Auto.VarableDictionary.ContainsKey((dev.Name + "_" + varable.Address).ToUpper().Trim()))//符合就加入到字典集合中
                    {
                        Auto.VarableDictionary.Add((dev.Name + "_" + varable.Address).ToUpper().Trim(), varable);
                    }
                    else
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个地址有重复";
                    }
                }
            }

            //todu 2.验证串口设备
            foreach (var dev in allDeviceList.SerialPortDeviceList)//遍历所有网口设备
            {
                if (dev.AllVarableList.Count <= 0)
                {
                    result.IsSucceed = false;
                    result.Err = $"{dev.Name}一个变量都没有";
                }

                foreach (var varable in dev.AllVarableList)//遍历设备的所有变量
                {
                    //todo 优化这里调用每个设备的验证地址是否合法的方法
                    if (!dev.VarableAddressList.ContainsKey(varable.Address))
                    {
                        dev.VarableAddressList.Add(varable.Address, varable.DataType);
                    }
                    else
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个变量地址有重复";
                        continue;
                    }
                    if (!dev.AllVarableDictionary.ContainsKey((dev.Name + "_" + varable.Address).ToUpper().Trim()))//符合就加入到字典集合中
                    {
                        dev.AllVarableDictionary.Add((dev.Name + "_" + varable.Address).ToUpper().Trim(), varable);
                    }
                    else//不符合就将结果返回false
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个地址有重复";
                    }
                    if (!Auto.ValueDictionary.ContainsKey((dev.Name + "_" + varable.Address).ToUpper().Trim()))//符合就加入到字典集合中
                    {
                        Auto.ValueDictionary.Add((dev.Name + "_" + varable.Address).ToUpper().Trim(), "NA");
                    }
                    else
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个地址有重复";
                    }
                    if (!Auto.VarableDictionary.ContainsKey((dev.Name + "_" + varable.Address).ToUpper().Trim()))//符合就加入到字典集合中
                    {
                        Auto.VarableDictionary.Add((dev.Name + "_" + varable.Address).ToUpper().Trim(), varable);
                    }
                    else
                    {
                        result.IsSucceed = false;
                        result.Err = dev.Name + "_" + varable.Address + ":这个地址有重复";
                    }
                }
            }

            if (result.IsSucceed)
            {
                Auto.IsReady = true;
                AllDeviceList = allDeviceList;
            }
            return result;
        }

        #region winform服务

        private static Timer timer = null;

        private static List<Control> controlslist = new List<Control>();
        private static readonly object ctrlistlock = new object();
        private static readonly object timerlock = new object();

        /// <summary>
        ///
        /// </summary>
        /// <param name="ctr"></param>
        /// <param name="interval"></param>
        /// <returns></returns>
        public static Result ServiceWinform(Control ctr, int interval = 100)
        {
            lock (timerlock)
            {
                if (timer == null)
                {
                    timer = new Timer();
                    timer.Interval = interval;//tudo 被固定了,可以考虑放在外面,随时都可以改
                    timer.Tick += Timer_Tick;
                }
                timer.Enabled = true;
            }
            lock (ctrlistlock)
            {
                ctr.Disposed += Ctr_Disposed;
                if (controlslist.IndexOf(ctr) < 0)
                {
                    controlslist.Add(ctr);
                }
            }
            Result result = new Result();

            return result;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Timer_Tick(object sender, EventArgs e)
        {
            lock (ctrlistlock)
            {
                if (controlslist.Count <= 0)
                {
                    timer.Stop();
                }
                foreach (Control item in controlslist)
                {
                    UpdateView(item);
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private static void Ctr_Disposed(object sender, EventArgs e)
        {
            lock (controlslist)
            {
                var x = controlslist.Remove((Control)sender);
            }
        }

        /// <summary>
        /// 更新界面
        /// </summary>
        /// <param name="ctr"></param>
        private static void UpdateView(Control ctr)
        {
            // ctr.Text = DateTime.Now.ToString();
            if (ctr.AccessibleName != null && Auto.ValueDictionary.ContainsKey(ctr.AccessibleName.Trim().ToUpper()))
            {
                ctr.Text = Auto.ValueDictionary[ctr.AccessibleName.Trim().ToUpper()];
            }
            if (ctr.HasChildren)
            {
                foreach (Control cctr in ctr.Controls)
                {
                    UpdateView(cctr);
                }
            }
            Console.WriteLine(DateTime.Now.ToString() + "     " + controlslist.Count);
        }

        #endregion winform服务
    }

    /// <summary>
    ///
    /// </summary>
    [XmlRoot("所有设备集合")]
    public class AllDeviceList
    {
        /// <summary>
        ///
        /// </summary>
        [XmlArrayItem(typeof(SiemensClient))]
        [XmlArrayItem(typeof(MitsubishiClient))]
        [XmlArrayItem(typeof(OmronFinsClient))]
        [XmlArrayItem(typeof(AllenBradleyClient))]
        [XmlArrayItem(typeof(ModbusTcpClient))]
        [XmlArrayItem(typeof(ModbusRtuOverTcpClient))]
        [XmlArray("网络设备集合")]
        public List<SocketBase> SocketDeviceList { get; set; } = new List<SocketBase>();

        /// <summary>
        ///
        /// </summary>

        // [XmlArrayItem(typeof(ModbusSerialBase))]
        [XmlArrayItem(typeof(ModbusRtuClient))]
        [XmlArrayItem(typeof(ModbusAsciiClient))]
        [XmlArray("串口设备集合")]
        public List<SerialPortBase> SerialPortDeviceList { get; set; } = new List<SerialPortBase>();

        /// <summary>
        /// 遍历所有的设备,根据是否激活来启动
        /// </summary>
        /// <param name="Alarm">变量激发了报警将要进行的委托</param>
        /// <param name="WarningLog">连接异常,读取异常等将要进行的委托</param>
        /// <returns>为false并不表示所有的都启动失败,具体成功与否需要看err信息</returns>
        public Result Start(Action<Variable, AlarmEventArgs> Alarm = null, LoggerDelegate WarningLog = null)
        {
            Result result = new Result();
            //开始启动socket设备
            foreach (var dev in SocketDeviceList)
            {
                if (dev.IsActive)
                {
                    dev.Alarmer = Alarm;
                    dev.Loger = WarningLog;
                    result.SetErrInfo(dev.Start());
                    dev.Loger?.Invoke(dev.Name + "启动配置:" + (result.IsSucceed ? "正确" : "错误") + (result.IsSucceed ? ";正在后台启动中...." : "请主要检查变量地址,或者查看错误信息"), result.Exception);
                }
            }
            //todo 开启serialport设备
            foreach (var dev in SerialPortDeviceList)
            {
                if (dev.IsActive)
                {
                    dev.Alarming = Alarm;
                    dev.Loger = WarningLog;
                    result.SetErrInfo(dev.Start());
                    dev.Loger?.Invoke(dev.Name + "启动配置:" + (result.IsSucceed ? "正确" : "错误") + (result.IsSucceed ? ";正在后台启动中...." : "请主要检查变量地址,或者查看错误信息"), result.Exception);
                }
            }
            return result;
        }

        /// <summary>
        /// 循环所有的设备,然后调用他们的停止方法
        /// </summary>
        /// <returns></returns>
        public Result Stop()
        {
            Result result = new Result();
            if (SocketDeviceList.Count > 0)
            {
                foreach (var item in SocketDeviceList)
                {
                    result.SetErrInfo(item.Stop());
                }
            }
            //todo 串口停止
            if (SerialPortDeviceList.Count > 0)
            {
                foreach (var item in SerialPortDeviceList)
                {
                }
            }
            return result;
        }
    }
}